﻿using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Nvigorate.Relational.Mapping;
using Nvigorate.Relational.Mapping.Index;
using Nvigorate.TestObjects;

namespace Nvigorate.Test.Relational.Binding
{
    [TestClass]
    public class MapIndexFactoryTests
    {
        #region Fixture Methods

        [TestInitialize]
        public void Initialize()
        {
            new MapIndexFactory().ResetMapIndex();
        }

        #endregion

        #region CreateMapIndex Tests

        [TestMethod]
        public void CreateMapIndex_Called_ReturnsMapIndex()
        {
            // Arrange
            var source = "EmployeeTable";
            var target = "EmployeeClass";
            var maps = new[]
                           {
                               new BindingMap
                                   {RepositoryName = source, TargetType = typeof (Employee), TargetName = target}
                           };
            var mapLoader = CreateMapLoader(maps);

            // Act
            var factory = new MapIndexFactory(new[] {mapLoader});
            var actual = factory.CreateMapIndex();

            // Assert
            Assert.IsTrue(actual.HasBindingMap(typeof (Employee), source));
            var map = actual[source, typeof (Employee)];
            Assert.AreEqual(target, map.TargetName);
        }

        [TestMethod]
        public void CreateMapIndex_CalledTwice_OnlyLoadsMapIndexOnce()
        {
            // Act
            var factory = new MapIndexFactoryHarness();
            factory.CreateMapIndex();
            var actual = factory.CreateMapIndex();

            // Assert
            Assert.IsNotNull(actual);
            Assert.AreEqual(1, factory.MapLoadedCount);
        }

        [TestMethod]
        public void CreateMapIndex_UsingMultipleMapLoaders_GetMapsFromAllLoaders()
        {
            // Arrange
            var source1 = "EmployeeTable";
            var target1 = "EmployeeClass";
            var map1 = new BindingMap {RepositoryName = source1, TargetType = typeof (Employee), TargetName = target1};
            var loader1 = CreateMapLoader(new[] {map1});

            var source2 = "CustomerTable";
            var target2 = "CustomerClass";
            var map2 = new BindingMap {RepositoryName = source2, TargetType = typeof (Person), TargetName = target2};
            var loader2 = CreateMapLoader(new[] {map2});

            // Act
            var factory = new MapIndexFactory(new[] {loader1, loader2});
            var actual = factory.CreateMapIndex();

            // Assert
            Assert.IsTrue(actual.HasBindingMap(typeof (Employee), source1));
            Assert.IsTrue(actual.HasBindingMap(typeof (Person), source2));
        }

        #endregion

        #region PopulateMapLoaders Tests

        [TestMethod]
        public void PopulateMapLoaders_WithBindingElements_CreatesMapElementMapLoader()
        {
            // Arrange
            var section = new MapConfigurationSection
                              {
                                  BindingElements = new BindingMapCollection {new MapElement()}
                              };

            // Act
            var factory = new MapIndexFactoryHarness(section);
            var actual = factory.GetMapLoadersFromConfiguration().ToList();

            // Assert
            Assert.AreEqual(1, actual.Count);
            Assert.IsInstanceOfType(actual[0], typeof (MapElementMapLoader));
        }

        [TestMethod]
        public void PopulateMapLoaders_WithScanTargets_CreatesScanTargetMapLoader()
        {
            // Arrange
            var section = new MapConfigurationSection
                              {
                                  MapScanCollection = new ScanCollection
                                                          {
                                                              new ScanTarget()
                                                          }
                              };

            // Act
            var factory = new MapIndexFactoryHarness(section);
            var actual = factory.GetMapLoadersFromConfiguration().ToList();

            // Assert
            Assert.AreEqual(1, actual.Count);
            Assert.IsInstanceOfType(actual[0], typeof (ScanTargetMapLoader));
        }

        [TestMethod]
        public void PopulateMapLoaders_WithScanTargetsAndMapElements_CreatesBothLoaders()
        {
            // Arrange
            var section = new MapConfigurationSection
                              {
                                  BindingElements = new BindingMapCollection {new MapElement()},
                                  MapScanCollection = new ScanCollection {new ScanTarget()}
                              };

            // Act
            var factory = new MapIndexFactoryHarness(section);
            var actual = factory.GetMapLoadersFromConfiguration().ToList();

            // Assert
            Assert.AreEqual(2, actual.Count);
            Assert.AreEqual(1, actual.Count(m => m.GetType() == typeof (ScanTargetMapLoader)));
            Assert.AreEqual(1, actual.Count(m => m.GetType() == typeof (MapElementMapLoader)));
        }

        #endregion

        #region Private Methods

        private IMapLoader CreateMapLoader(BindingMap[] maps)
        {
            var mock = new Mock<IMapLoader>();
            mock.Expect(m => m.GetMaps()).Returns(maps);
            return mock.Object;
        }

        #endregion

        #region Nested Types

        private class MapIndexFactoryHarness : MapIndexFactory
        {
            #region Constructors

            public MapIndexFactoryHarness()
            {
            }

            public MapIndexFactoryHarness(MapConfigurationSection section) : base(section)
            {
            }

            #endregion

            #region Public Properties

            public int MapLoadedCount { get; set; }

            #endregion

            #region Public Methods

            public new IEnumerable<IMapLoader> GetMapLoadersFromConfiguration()
            {
                return base.GetMapLoadersFromConfiguration();
            }

            #endregion

            #region Protected Methods

            protected override IMapIndex LoadMapIndex()
            {
                MapLoadedCount++;
                return new MapIndex();
            }

            #endregion
        }

        #endregion
    }
}